
open Ast

let transform ast =

	let rec transform_sourceElement = function
		| Class_ast.AStatement stmt -> AStatement (transform_statement stmt)
		(*| Class_ast.AFunctionDeclaration funcDecl ->  AFunctionDeclaration (transform_functionDeclaration funcDecl) *) (* Modif #1 *)

	and transform_functionDeclaration (is_private,is_meta,is_inline,ident,params,body) = 
		is_private, is_meta, is_inline, ident, params, (transform_functionBody body)
		
	and transform_functionExpression (is_inline, ident, params, body) =
		is_inline, ident, params, (transform_functionBody body)
		
	and transform_functionBody body = 
		List.map transform_sourceElement body

	(* Statements *)
	and transform_block stmts =
		List.map transform_statement stmts

	and transform_statement = function
		| Class_ast.ABlock block -> ABlock (transform_block block)
		| Class_ast.AVariableStatement vs -> AVariableStatement (transform_variableStatement vs)
		| Class_ast.AConstStatement cs -> AConstStatement (transform_constStatement cs)
		| Class_ast.AFuncDeclaration fd -> AFuncDeclaration (transform_functionDeclaration fd)	(* Modif #1 *)
		| Class_ast.AClassStatement cs -> ABlock (transform_classStatement cs)
		| Class_ast.AEmptyStatement -> AEmptyStatement
		| Class_ast.AExpressionStatement expr -> AExpressionStatement (transform_expression expr)
		| Class_ast.AIfStatement stmt -> AIfStatement (transform_ifStatement stmt)
		| Class_ast.AIterationStatement stmt -> AIterationStatement (transform_iterationStatement stmt)
		| Class_ast.AContinueStatement e -> AContinueStatement e
		| Class_ast.ABreakStatement e -> ABreakStatement e
		| Class_ast.AReturnStatement expr -> AReturnStatement (transform_optionalExpression expr)
		| Class_ast.AWithStatement stmt -> AWithStatement (transform_withStatement stmt)
		| Class_ast.ALabelledStatement stmt -> ALabelledStatement (transform_labelledStatement stmt)
		| Class_ast.ASwitchStatement stmt -> ASwitchStatement (transform_switchStatement stmt)
		| Class_ast.AThrowStatement expr -> AThrowStatement (transform_expression expr)
		| Class_ast.ATryStatement stmt -> ATryStatement (transform_tryStatement stmt)
		| Class_ast.ADebuggerStatement -> ADebuggerStatement

	and transform_variableStatement (is_private, is_meta, varDecls) =
		is_private, is_meta, (List.map transform_variableDeclaration varDecls)
		
	and transform_classStatement (is_private, is_meta, ident, extends, classElements) =
		let (funcDecl,classElements) = List.fold_right (fun decl (a,classElements) -> 
			match decl with
				| Class_ast.ACFuncDeclaration decl ->
					let _, _, _, i, _, _ = decl in
					if i = ident then (AFuncDeclaration (transform_functionDeclaration decl), classElements) 
					else (a, (Class_ast.ACFuncDeclaration decl)::classElements)
				| _ -> a, (decl::classElements)
			) classElements (AFuncDeclaration (is_private,is_meta,true,ident,[],[]), [] ) in
		let funcDecl = if is_private then
			funcDecl
		else
			match funcDecl with
				| AFuncDeclaration (is_private, is_meta, is_inline, ident, params, body ) ->
					AVariableStatement (is_private, is_meta, [ident,Some (AAssignOp(
					ANewExpression(
						AMemberExpression(
							AMemberDotExpr(
								APrimaryExpr(AThis),ident)))
				,"=", ACondExpr(
					ACondBinaryExpr(
						AUnaryExpr(
							APostfixExpr(
								ALeftHandSide(
									ANewExpression(
											AMemberExpression(
												AFunctionExpr(is_inline,None, params, body)
											)))))))))])
				| _ -> AEmptyStatement;
			
		in
		let lst = (match extends with
			| None -> [funcDecl]
			| Some lst -> 
				let assign = AExpressionStatement([AAssignOp(
					ANewExpression(
						AMemberExpression(
							AMemberDotExpr(
								APrimaryExpr(AIdentifier ident),"prototype")))
				,"=", ACondExpr(
					ACondBinaryExpr(
						AUnaryExpr(
							APostfixExpr(
								ALeftHandSide(
									ANewExpression(
										ANewMemberExpression(
											AMemberExpression(get_list_as_member lst))))))))
				)]) in
				[funcDecl;assign]) in
		List.fold_left (transform_classElement ident) lst classElements
	
	and get_list_as_member lst =
			match lst with
				| [] -> APrimaryExpr(AIdentifier("")) (* Should never happen *)
				| a::lst -> List.fold_left (fun b a -> AMemberDotExpr(b,a)) (APrimaryExpr(AIdentifier(a))) lst
		

	and transform_classElement ident lst = function
		| Class_ast.ACVariableStatement (_,_,varDecls) -> lst@(List.map (transform_classVariableDeclaration ident) varDecls)
		| Class_ast.ACConstStatement (_,_,varDecls) -> lst@(List.map (transform_classVariableDeclaration ident) varDecls)
		| Class_ast.ACFuncDeclaration funcDecl -> lst@[transform_classFunctionDeclaration ident funcDecl]
	
	and transform_classVariableDeclaration ident (name,init) =
		AExpressionStatement([AAssignOp(
			ANewExpression(
				AMemberExpression(
					AMemberDotExpr(
						AMemberDotExpr(
						APrimaryExpr(AIdentifier ident),"prototype"),name)))
		,"=", (match init with
				| None -> ACondExpr(
					ACondBinaryExpr(
						AUnaryExpr(
							APostfixExpr(
								ALeftHandSide(
									ANewExpression(
										AMemberExpression(
											APrimaryExpr(
												AIdentifier "undefined"))))))))
				| Some expr -> transform_assignmentExpression expr
				)
		)])
	
	and transform_classFunctionDeclaration ident funcDecl =
		let _,_,is_inline,name,params,body = funcDecl in
		AExpressionStatement([AAssignOp(
			ANewExpression(
				AMemberExpression(
					AMemberDotExpr(
						AMemberDotExpr(
						APrimaryExpr(AIdentifier ident),"prototype"),name)))
		,"=", 
		ACondExpr(
			ACondBinaryExpr(
				AUnaryExpr(
					APostfixExpr(
						ALeftHandSide(
							ANewExpression(
								AMemberExpression(
									AFunctionExpr(transform_functionExpression (is_inline,Some name, params,body)
									))))))))
		)])

	and transform_constStatement (is_private, is_meta, varDecls) =
		is_private, is_meta, (List.map transform_variableDeclaration varDecls)

	and transform_variableDeclaration (ident, init) =
		ident, (match init with
			| None -> None
			| Some expr -> Some (transform_assignmentExpression expr))

	and transform_ifStatement = function
		| Class_ast.AIfElse (expr, stmt1, stmt2) -> 
			let expr = transform_expression expr in
			let stmt1 = transform_statement stmt1 in
			let stmt2 = transform_statement stmt2 in
			AIfElse (expr, stmt1, stmt2)
		| Class_ast.AIf (expr, stmt) -> 
			let expr = transform_expression expr in
			let stmt = transform_statement stmt in
			AIf (expr, stmt)

	and transform_iterationStatement = function
		| Class_ast.ADoWhile (stmt, expr) ->
			let stmt = transform_statement stmt in
			let expr = transform_expression expr in
			ADoWhile (stmt, expr)
		| Class_ast.AWhile (expr, stmt) -> 
			let expr = transform_expression expr in
			let stmt = transform_statement stmt in
			AWhile (expr, stmt)
		| Class_ast.AFor (expr1, expr2, expr3, stmt) ->
			let expr1 = transform_optionalExpression expr1 in
			let expr2 = transform_optionalExpression expr2 in
			let expr3 = transform_optionalExpression expr3 in
			let stmt = transform_statement stmt in
			AFor (expr1, expr2, expr3, stmt )
		| Class_ast.AForVar (is_meta, varDecls, expr1, expr2, stmt) ->
			let varDecls = List.map transform_variableDeclaration varDecls in
			let expr1 = transform_optionalExpression expr1 in
			let expr2 = transform_optionalExpression expr2 in
			let stmt = transform_statement stmt in
			AForVar (is_meta, varDecls, expr1, expr2, stmt)
		| Class_ast.AForIn (lhs, expr, stmt) ->
			let lhs = transform_leftHandSideExpression lhs in
			let expr = transform_expression expr in
			let stmt = transform_statement stmt in
			AForIn (lhs, expr, stmt)
		| Class_ast.AForVarIn (is_meta, varDecl, expr, stmt) ->
			let varDecl = transform_variableDeclaration varDecl in
			let expr = transform_expression expr in
			let stmt = transform_statement stmt in
			AForVarIn (is_meta, varDecl, expr, stmt)

	and transform_optionalExpression = function
		| None -> None
		| Some expr -> Some (transform_expression expr)

	and transform_withStatement (expr, stmt) =
		(transform_expression expr), (transform_statement stmt)
		
	and transform_switchStatement (expr, caseBlock) =
		(transform_expression expr), (transform_caseBlock caseBlock)

	and transform_caseBlock = function
		| Class_ast.ACasesNoDefault caseClauses -> 
			let caseClauses = List.map transform_caseClause caseClauses in
			ACasesNoDefault caseClauses
		| Class_ast.ACasesDefault (caseClauses1, defaultClause, caseClauses2) ->
			let caseClauses1 = List.map transform_caseClause caseClauses1 in
			let defaultClause = List.map transform_statement defaultClause in
			let caseClauses2 = List.map transform_caseClause caseClauses2 in
			ACasesDefault (caseClauses1, defaultClause, caseClauses2)

	and transform_caseClause (expr, stmts) = 
		(transform_expression expr, List.map transform_statement stmts)

	and transform_labelledStatement (ident, stmt) =
		ident, (transform_statement stmt)

	and transform_tryStatement = function
		| Class_ast.ATryCatch (block, catch) ->
			ATryCatch (transform_block block, transform_catch catch)
		| Class_ast.ATryFinally (block, finally) ->
			ATryFinally (transform_block block, transform_block finally)
		| Class_ast.ATryCatchFinally (block, catch, finally) ->
			ATryCatchFinally (transform_block block, transform_catch catch, transform_block finally)
			
	and transform_catch (ident, block) =
		ident, (transform_block block)

	(* Literals *)
	and transform_literal = function
		| Class_ast.ANull -> ANull
		| Class_ast.ABooleanLiteral b -> ABooleanLiteral b
		| Class_ast.ALitNumericLiteral s -> ALitNumericLiteral s
		| Class_ast.ALitStringLiteral s -> ALitStringLiteral s
		| Class_ast.ARegularExpressionLiteral regex -> ARegularExpressionLiteral regex

	(* Expressions *)
	and transform_primaryExpression = function
		| Class_ast.AThis -> AThis
		| Class_ast.AIdentifier ident -> AIdentifier ident
		| Class_ast.ALiteral literal -> ALiteral (transform_literal literal)
		| Class_ast.AArrayLiteral literal -> AArrayLiteral (transform_arrayLiteral literal)
		| Class_ast.AObjectLiteral literal -> AObjectLiteral (transform_objectLiteral literal)
		| Class_ast.AExpression expr -> AExpression (transform_expression expr)

	and transform_arrayLiteral = function
		| Class_ast.AElisions elisions -> AElisions (List.map transform_elision elisions)
		| Class_ast.AElements elements -> AElements (List.map transform_element elements)
		| Class_ast.AElementsAndElisions (elements, elisions) ->
			let elements = List.map transform_element elements in
			let elisions = List.map transform_elision elisions in
			AElementsAndElisions (elements, elisions)

	and transform_elision _ = AElision

	and transform_element (elisions, expr) = 
		(List.map transform_elision elisions), (transform_assignmentExpression expr)

	and transform_objectLiteral props = 
		List.map transform_propertyAssignment props

	and transform_propertyAssignment = function
		| Class_ast.APropAssignment (prop,expr) -> 
			let prop = transform_propertyName prop in
			let expr = transform_assignmentExpression expr in
			APropAssignment (prop, expr)
		| Class_ast.APropGet (prop, body) ->
			let prop = transform_propertyName prop in
			let body = transform_functionBody body in
			APropGet (prop, body)
		| Class_ast.APropSet (prop, idents, body) ->
			let prop = transform_propertyName prop in
			let body = transform_functionBody body in
			APropSet (prop, idents, body)

	and transform_propertyName = function
		| Class_ast.AIdentifierName s -> AIdentifierName s
		| Class_ast.AStringLiteral s -> AStringLiteral s
		| Class_ast.ANumericLiteral s -> ANumericLiteral s

	and transform_memberExpression = function
		| Class_ast.APrimaryExpr expr -> APrimaryExpr (transform_primaryExpression expr)
		| Class_ast.AFunctionExpr expr -> AFunctionExpr (transform_functionExpression expr)
		| Class_ast.AMemberBracketExpr (expr1, expr2) -> 
			let expr1 = transform_memberExpression expr1 in
			let expr2 = transform_expression expr2 in
			AMemberBracketExpr (expr1, expr2)
		| Class_ast.AMemberDotExpr (expr, ident) ->
			AMemberDotExpr (transform_memberExpression expr, ident)
		| Class_ast.AMemberNewExpr (expr, args) ->
			let expr = transform_memberExpression expr in
			let args = transform_arguments args in
			AMemberNewExpr (expr, args)

	and transform_newExpression = function
		| Class_ast.AMemberExpression expr ->
			AMemberExpression (transform_memberExpression expr)
		| Class_ast.ANewMemberExpression expr ->
			ANewMemberExpression (transform_newExpression expr)

	and transform_callExpression = function
		| Class_ast.ACallMemberArgs (expr,args) ->
			let expr = transform_memberExpression expr in
			let args = transform_arguments args in
			ACallMemberArgs (expr, args)
		| Class_ast.ACallExpressionArgs (expr, args) ->
			let expr = transform_callExpression expr in
			let args = transform_arguments args in
			ACallExpressionArgs (expr, args)
		| Class_ast.ACallExpressionExpr (expr1, expr2) ->
			let expr1 = transform_callExpression expr1 in
			let expr2 = transform_expression expr2 in
			ACallExpressionExpr (expr1, expr2)
		| Class_ast.ACallExpressionIdent (expr, ident) ->
			let expr = transform_callExpression expr in
			ACallExpressionIdent (expr, ident)

	and transform_arguments args = 
		List.map transform_assignmentExpression args

	and transform_leftHandSideExpression = function
		| Class_ast.ANewExpression expr ->
			let expr = transform_newExpression expr in
			ANewExpression expr
		| Class_ast.ACallExpression expr ->
			let expr = transform_callExpression expr in
			ACallExpression expr

	and transform_postfixExpression = function
		| Class_ast.ALeftHandSide lhs ->
			let lhs = transform_leftHandSideExpression lhs in
			ALeftHandSide lhs
		| Class_ast.ALeftHandSideOp (lhs,op) ->
			let lhs = transform_leftHandSideExpression lhs in
			ALeftHandSideOp (lhs,op)

	and transform_unaryExpression = function
		| Class_ast.APostfixExpr expr ->
			let expr = transform_postfixExpression expr in
			APostfixExpr expr
		| Class_ast.AOpUnaryExpr (op,expr) ->
			let expr = transform_unaryExpression expr in
			AOpUnaryExpr (op, expr)

	and transform_binaryExpression = function
		| Class_ast.AUnaryExpr expr -> AUnaryExpr (transform_unaryExpression expr)
		| Class_ast.ABinaryExpr (op, expr1, expr2) ->
			let expr1 = transform_binaryExpression expr1 in
			let expr2 = transform_binaryExpression expr2 in
			ABinaryExpr (op, expr1, expr2)

	and transform_conditionalExpression = function
		| Class_ast.ACondBinaryExpr expr ->
			ACondBinaryExpr (transform_binaryExpression expr)
		| Class_ast.ACondTernaryExpr (expr1, expr2, expr3) ->
			let expr1 = transform_binaryExpression expr1 in
			let expr2 = transform_assignmentExpression expr2 in
			let expr3 = transform_assignmentExpression expr3 in
			ACondTernaryExpr (expr1, expr2, expr3)

	and transform_assignmentExpression = function
		| Class_ast.ACondExpr expr -> ACondExpr (transform_conditionalExpression expr)
		| Class_ast.AAssignOp (lhs,op,expr) ->
			let lhs = transform_leftHandSideExpression lhs in
			let expr = transform_assignmentExpression expr in
			AAssignOp (lhs,op,expr)

	and transform_expression exprs = 
		List.map transform_assignmentExpression exprs

	in
	List.map transform_sourceElement ast

